package persistencia;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.logging.Logger;
import javax.servlet.http.HttpServlet;
import modelo.Concierto;
import modelo.Entrada;
import modelo.Usuario;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class PersistenciaBD extends HttpServlet implements PersistenciaInterface  {

    private static final PersistenciaBD instance = new PersistenciaBD();

    private PersistenciaBD() {
    }

    public static PersistenciaBD getInstance() {
        return instance;
    }
    
    //Añade un cliente a la BD
    @Override
    public boolean addClient(Usuario client) {
        boolean correcto;
        try{
            client.setPassword(convertirMD5(client.getPassword()));
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
            Session s = sf.openSession(); 
            s.beginTransaction();
            s.save(client);
            s.getTransaction().commit();
            s.close(); 
            sf.close();
            correcto = true;
        }
        catch(HibernateException e){
            correcto = false;
        }
        return correcto;
    }

    //Devuelve un objeto Client si el username y el password existen y coinciden con algun usuario
    @Override
    public Usuario getClient(String username, String pass) {
        SessionFactory sf = new Configuration().configure().buildSessionFactory(); 
        Session s = sf.openSession();
        
        try
        {
            String passHash = convertirMD5(pass);
            Usuario usu = (Usuario) s.createQuery("from modelo.Usuario where username = '"+username+"' and password = '"+passHash+"'").uniqueResult();
            s.close(); 
            sf.close();
            return usu;
        }
        catch(HibernateException e)
        {
            s.close(); 
            sf.close();
            return null;
        }
    }
    
    //Devuelve un objeto Client si el username existe en la BD
    @Override
    public Usuario getClient(String username) {
        SessionFactory sf = new Configuration().configure().buildSessionFactory(); 
        Session s = sf.openSession();
        try
        {
            Usuario usu = (Usuario) s.createQuery("from modelo.Usuario where username = '"+username+"'").uniqueResult();
            s.close(); 
            sf.close();
            return usu;
        }
        catch(HibernateException e)
        {
            s.close(); 
            sf.close();
            return null;
        }
    }
    
    //Devuelve todos los usuarios que hay registrados en la BD
    @Override
    public ArrayList<Usuario> getClients() {
         
        ArrayList<Usuario> usuarios = null;
        SessionFactory sf = new Configuration().configure().buildSessionFactory(); 
        Session s = sf.openSession();
        try
        {
            usuarios = (ArrayList<Usuario>) s.createQuery("from modelo.Usuario where permiso=0").list();
            s.close(); 
            sf.close();
        }
        catch(HibernateException e)
        {
            s.close(); 
            sf.close();
        }
        return usuarios;
    }

    //Borra el usuario con el username pasado como parametro
    @Override
    public boolean deleteClient(String username) {
        boolean correcto;
        try{
            Usuario user = getClient(username);
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
            Session s = sf.openSession(); 
            s.beginTransaction();
            s.delete(user);
            s.getTransaction().commit();
            s.close(); 
            sf.close();
            correcto = true;
        }
        catch(HibernateException e){
            correcto = false;
        }
        return correcto;
    }
    
    //Añade credito al usuario pasado como parametro
    @Override
    public boolean addCredito(Usuario user, float credito) {
        boolean correcto;
        try{
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
            Session s = sf.openSession(); 
            user.setCredito(user.getCredito()+credito);
            s.beginTransaction();
            s.update(user);
            s.getTransaction().commit();
            s.close(); 
            sf.close();
            correcto = true;
        }
        catch(HibernateException e){
            correcto = false;
        }
        return correcto;
    }
    
    //Añade un concierto a la BD
    @Override
    public boolean addConcierto(Concierto concierto) {
        boolean correcto;
        try{
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
            Session s = sf.openSession(); 
            s.beginTransaction();
            s.save(concierto);
            s.getTransaction().commit();
            s.close(); 
            sf.close();
            correcto = true;
        }
        catch(HibernateException e){
            correcto = false;
        }
        return correcto;
    }

    //Devuelve un concierto pasandole como parametro la descripcion
    @Override
    public Concierto getConcierto(String descripcion) {
        SessionFactory sf = new Configuration().configure().buildSessionFactory(); 
        Session s = sf.openSession();
        try
        {
            Concierto concier = (Concierto) s.createQuery("from modelo.Concierto where descripcion = '"+descripcion+"'").uniqueResult();
            s.close(); 
            sf.close();
            return concier;
        }
        catch(HibernateException e)
        {
            s.close(); 
            sf.close();
            return null;
        }
    }

    //Devuelve todos los conciertos que hay en la BD
    @Override
    public ArrayList<Concierto> getConciertos() {
        ArrayList<Concierto> lista = null;
        SessionFactory sf = new Configuration().configure().buildSessionFactory(); 
        Session s = sf.openSession();
        try
        {
            Query q = s.createQuery("FROM modelo.Concierto");
            lista = (ArrayList<Concierto>) q.list();
            s.close(); 
            sf.close();
        }
        catch(HibernateException e)
        {
            s.close(); 
            sf.close();
        }
        return lista;
    }

    //Devuelve los conciertos de la BD que coinciden con los parametros recibidos
    @Override
    public ArrayList<Concierto> getConciertos(String descripcion, String lugar, String genero, String aforoMayor, String aforoMenor, String precioMayor, String precioMenor) {
        ArrayList<Concierto> lista;
        SessionFactory sf = new Configuration().configure().buildSessionFactory(); 
        Session s = sf.openSession();
        String consulta = "FROM modelo.Concierto ";
        String condiciones = "";

        if (!descripcion.equals(""))
            condiciones+=" descripcion='"+descripcion+"' ";
        if(!lugar.equals("")){
            if (!condiciones.equals(""))
                condiciones+="AND ";
            condiciones+="lugar='"+lugar+"' ";  
        }
        if(!genero.equals("")){
            if (!condiciones.equals(""))
                condiciones+="AND ";
            condiciones+="genero='"+genero+"' ";  
        }
        if(!aforoMayor.equals("")){
            if (!condiciones.equals(""))
                condiciones+="AND ";
            int intAforoMayor = Integer.parseInt(aforoMayor);
            condiciones+="aforo>"+intAforoMayor;  
        }
        if (!aforoMenor.equals("")){
            if (!condiciones.equals(""))
                condiciones+=" AND ";
            int intAforoMenor = Integer.parseInt(aforoMenor);
            condiciones+="aforo<"+intAforoMenor;
        }
        if(!precioMayor.equals("")){
            if (!condiciones.equals(""))
                condiciones+="AND ";
            float intPrecioMayor = Float.parseFloat(precioMayor);
            condiciones+="precio>"+intPrecioMayor;  
        }
        if (!precioMenor.equals("")){
            if (!condiciones.equals(""))
                condiciones+=" AND ";
            float intPrecioMenor = Float.parseFloat(precioMenor);
            condiciones+="precio<"+intPrecioMenor;
        }
        if (!condiciones.equals(""))
        {
            consulta = consulta + "WHERE " + condiciones;
        }
        Query q = s.createQuery(consulta);
        lista = (ArrayList<Concierto>) q.list();
        s.close(); 
        sf.close();
        return lista;
    }
    
    //Borra el concierto con la descripcion pasada como parametro
    @Override
    public boolean deleteConcierto(String desc) {
        boolean correcto;
        try{
            Concierto conc = getConcierto(desc);
            SessionFactory sf = new Configuration().configure().buildSessionFactory();
            Session s = sf.openSession(); 
            s.beginTransaction();
            s.delete(conc);
            s.getTransaction().commit();
            s.close(); 
            sf.close();
            correcto = true;
        }
        catch(HibernateException e){
            correcto = false;
        }
        return correcto;
    }
    
    //Añade una entrada a la BD
    @Override
    public boolean addEntrada(Entrada entrada) {
        boolean correcto;
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        try{
            s.beginTransaction();
            s.save(entrada);
            s.getTransaction().commit();
            s.close(); 
            sf.close();
            correcto = true;
        }
        catch(HibernateException e){
            s.getTransaction().rollback();
            s.close();
            sf.close();
            correcto = false;
        }
        return correcto;
    }
    
    //Actualiza los registros del usuario y el concierto (el credito y las entradas disponibles)
    public boolean completarCompra(Usuario user, Concierto concert){
        boolean correcto;
        SessionFactory sf = new Configuration().configure().buildSessionFactory();
        Session s = sf.openSession();
        try{
            s.beginTransaction();
            s.update(user);
            s.update(concert);
            s.getTransaction().commit();
            s.close(); 
            sf.close();
            correcto = true;
        }
        catch(HibernateException e)
        {
            s.getTransaction().rollback();
            s.close();
            sf.close();
            correcto = false;
        }
        return correcto;
    }

    //Devuelve la entrada con el codigo pasado como parametro
    @Override
    public Entrada getEntrada(String codigo) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    //Devuelve todas las entradas que hay en la BD
    @Override
    public ArrayList<Entrada> getEntradas() {
        ArrayList<Entrada> entradas = null;
        SessionFactory sf = new Configuration().configure().buildSessionFactory(); 
        Session s = sf.openSession();
        try
        {
            entradas = (ArrayList<Entrada>) s.createQuery("from modelo.Entrada").list();
            s.close(); 
            sf.close();
        }
        catch(HibernateException e)
        {
            s.close(); 
            sf.close();
        }
        return entradas;
    }
    
    //Devuelve todas las entradas de un usuario
    @Override
    public ArrayList<Entrada> getEntradasUsuario(Usuario user) {
        ArrayList<Entrada> entradas = null;
        SessionFactory sf = new Configuration().configure().buildSessionFactory(); 
        Session s = sf.openSession();
        try
        {
            entradas = (ArrayList<Entrada>) s.createQuery("from modelo.Entrada where user_ID="+user.getUserId()).list();
            s.close(); 
            sf.close();
        }
        catch(HibernateException e)
        {
            s.close(); 
            sf.close();
        }
        return entradas;
    }

    //Devuelve todas las entradas de un concierto pasado como parametro
    @Override
    public ArrayList<Entrada> getEntradasConcierto(String descripcion) {
        ArrayList<Entrada> entradas = null;
        SessionFactory sf = new Configuration().configure().buildSessionFactory(); 
        Session s = sf.openSession();
        try
        {
            entradas = (ArrayList<Entrada>) s.createQuery("from modelo.Entrada where descripcion = '"+descripcion+"'").list();
            s.close(); 
            sf.close();
        }
        catch(HibernateException e)
        {
            s.close(); 
            sf.close();
        }
        return entradas;
    }
    
    //Convierte el String pasado como parametro a un String cifrado en MD5
    private String convertirMD5(String password){
        try{
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(password.getBytes());
            BigInteger Hash = new BigInteger(1 , md5.digest());
            return Hash.toString(16);
        }
        catch(NoSuchAlgorithmException e)
        {
            return "";
        }
    }
}